package club.beimeng.emailserver2.service.impl;

import club.beimeng.emailserver2.entity.EmailTransport;
import club.beimeng.emailserver2.service.EmailAccountService;
import club.beimeng.emailserver2.service.EmailService;
import club.beimeng.emailserver2.service.EmailTemplateService;
import club.beimeng.emailserver2.utils.ApplicationProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Logger;

@Order(4)
@Service
public class EmailServiceImpl implements EmailService, CommandLineRunner {

    Session session ; // 本地session环境
    Queue<MimeMessage> queue = new LinkedList<MimeMessage>(); // 任务队列。
    boolean threadRunning = false; // 规划线程运行状态
    private Logger logger = Logger.getLogger("初始化业务逻辑 - Email - Service");


    @Autowired
    public EmailTemplateService emailTemplateService;

    @Autowired
    public EmailAccountService emailAccountService;

    @Override
    public void run(String... args) throws Exception {// 进行初始化
        logger.info("开始初始化 - emailService");

        // 启动规划线程
        if (!threadRunning){
            threadRunning = true;
            new Thread(new PlanningTask()).start();
        }
    }


    @Override
    public boolean receiveTask(MimeMessage mimeMessage){ // 处理任务
        logger.info("添加任务到队列");
        // 将任务添加到队列之中
        return queue.offer(mimeMessage);
    }


    @Override
    public boolean receiveTask(String recipients, String title, String html) { // 处理任务方式 1
        // 生成 MimeMessage对象 调用上述方法 // 这里需要session对象
        MimeMessage mimeMessage = new MimeMessage(session);
        try {
            mimeMessage.setSubject(title);
            mimeMessage.setRecipients(Message.RecipientType.TO, recipients);
            mimeMessage.setContent(html,"text/html;charset=UTF-8");
        } catch (MessagingException e) {
            e.printStackTrace();
            return false;
        }

        return receiveTask(mimeMessage);
    }

    @Override
    public boolean receiveTask(String recipients,String title, String templateCode, Map<String,String> args) { // 处理方式 2
        // 根据templateCode 和 args 从templateService中获取到合适的html代码
        String innerHtml = emailTemplateService.getInnerHtml(templateCode,args);
        return receiveTask(recipients, title, innerHtml);
    }

    class PlanningTask implements Runnable{ // 规划任务线程

        List<EmailTransport> eTransports;
        int point;
        ExecutorService threadPool; // 线程池

        int emailNumbers = 0;

        public void init() {
            if (emailAccountService.getTransports() != null && emailAccountService.getTransports().size() != 0){
                // 获取载具列表，并生成一个同样大小的数组，用来标记载具的使用状态。
                // 所以需要一个指针，point 表示当前的轮询指向
                eTransports = emailAccountService.getTransports(); // 获取载具对象们
                point = 0;

                // 加载线程池
                int size = eTransports.size(); // 长度变成了0
                int threads = ApplicationProperties.THREADS;

                threadPool = Executors.newFixedThreadPool(
                        Math.min(size, threads)
                );
                emailAccountService.setChanged(false);
            }

        }

        public void refresh() {
            int times = 1;
            // 当EmailAccountService 中的载具列表已经发生改变 || 或者当前的载具对象已经过期
            while (emailAccountService.isChanged()){
                System.out.println("账号池发生变化，等待加载完成. 等待：" + times++);
                init();
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            if (!eTransports.get(point).getTransport().isConnected()){
                System.out.println("载具过期刷新载具");
                for (EmailTransport eTransport : eTransports) {

                    if (!eTransport.getTransport().isConnected()) {
                        try {
                            eTransport.getTransport().connect();
                        } catch (MessagingException e) {
                            e.printStackTrace();
                        }
                    }

                }
            }
        }

        @Override
        public void run() {
            logger.info("规划线程开始");

            init();

            int time = 0;

            while (threadRunning){

                refresh();

                while (queue.size() > 0){ // 当队列中有任务的时候，加速，直到任务都完成
                    logger.info("剩余任务队列长度：" + (queue.size()-1));
//                    logger.info("使用账号：" + eTransports.get(point).getEmailNumber());

                    if (!eTransports.get(point).isUsing()){
                        // 轮询任务列表，用不同的载具去处理任务。
                        EmailTransport etransport = eTransports.get(point); // 将point位置上的载具派出去
                        etransport.setUsing(true);

                        MimeMessage mimeMessage = queue.poll();

                        threadPool.submit(new SolveThread(etransport,mimeMessage));
                        emailNumbers++;

                        point++;
                        point %= eTransports.size();
                    }else {
                        logger.info("等待载具" + eTransports.get(point).getEmailNumber() + "完成任务");
                        break;// 跳出循环，等待一秒
                    }
                }
                try {
                    Thread.sleep(1000); // 等待一秒钟
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
//                System.out.print((".❤"));


                if (time % 60 == 0)
                    logRunning();
                time++;
                time%=10000000;
            }
        }

        private void logRunning() {
            // 打印当前线程池大小，使用者数量，状态.
            System.out.print("当前线程数量 = " + eTransports.size() + "  ");
            System.out.println("已经发送邮件【封】 = " + emailNumbers);

        }
    }

    class SolveThread implements Runnable{ // 处理任务线程
        EmailTransport emailTransport;
        MimeMessage mimeMessage;

        public SolveThread(EmailTransport etransport, MimeMessage mimeMessage) { // 我们这次使用载具发送邮件
            this.emailTransport = etransport;
            this.mimeMessage = mimeMessage;
        }
        @Override
        public void run() { // 这个线程的作用就是发送邮件
            Date dateBegin = new Date();
            String subject = null;
            try {
                subject = mimeMessage.getSubject();
            } catch (MessagingException e) {
                e.printStackTrace();
            }

            try {
                Transport ts = emailTransport.getTransport(); //通过session得到transport对象,用它装载Message(邮件本体对象)
                String emNumber = emailTransport.getEmailNumber();

                logger.info((emNumber + " | " + "准备发送邮件任务" + " | " + subject + " | " + "发送昵称" + " | " + ApplicationProperties.SIGNATURE + " | "
                        + "至" + " | " + Arrays.toString(mimeMessage.getAllRecipients())));

                String signature = javax.mail.internet.MimeUtility.encodeText(ApplicationProperties.SIGNATURE);

                mimeMessage.setFrom(new InternetAddress(signature +" <"+ emNumber + ">")); // 设置签名

                ts.sendMessage(mimeMessage, mimeMessage.getAllRecipients()); // 发送邮件

//                ts.close(); // 需要重复使用，所以不能关闭
            } catch (Exception e) {
                e.printStackTrace();
                logger.info(("发送失败 位置 sendEmail" + subject + " | " + emailTransport.getEmailNumber()));
            }
            logger.info(("邮件任务发送成功" + subject + " | " + emailTransport.getEmailNumber()));

            long l = new Date().getTime() - dateBegin.getTime();
            if (ApplicationProperties.TIME_DELAY != 0){ // 计算休眠时长
                try {
                    Thread.sleep(ApplicationProperties.TIME_DELAY * 1000L - l);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            emailTransport.setUsing(false); // 希望可以传送到上一级
        }
    }
}
